Skip to content

一、文件“秒传” & “断点续传”实现原理(面试级一句话 + 流程图)

1. 秒传(极速完成)

核心:“不用传,只认指纹”

  • 前端计算文件整体 SHA-256(或 MD5+文件大小)→ 得到 fileHash
  • 上传前先发 POST /check-hash
    json
    {"fileHash":"a8f2...","size":2147483648}
  • 服务端在 文件表 查 hash 是否存在
    • ✅ 存在:直接返回 文件 URL → 前端提示“秒传成功”
    • ❌ 不存在:走正常上传流程(普通 / 断点)

额外加速:

  • 秒传 + 分片 hash 索引:每个分片也算 hash,秒传失败时可直接复用已上传分片(秒传失败 → 秒级断点续传)

2. 断点续传(大文件不怕中断)

  • 分片:前端固定 4 MB 一块(可配置)
  • 顺序编号chunkIndex = 0..N-1
  • 并发上传:浏览器限制 6 并发,HTTP/2 可 100+
  • 校验:每片带 chunkHash,服务端校验 CRC32 确认无误再合并
  • 记录进度:Redis / MySQL 存 upload:fileHash -> BitSet(已上传分片),重启后秒级恢复

合并流程

text
所有分片上传完 → 服务端多线程顺序写入 → 生成最终文件 → 再次校验整体 hash → 返回 URL → 清理分片临时目录

秒传 + 断点组合时序

前端:计算 fileHash → check-hash → 秒传?
├─ 成功 → 完成
└─ 失败 → 分片上传(带 chunkHash)→ 中途断网 → 重新进入上传 → BitSet 告诉前端“已有 0,1,3 片”→ 只传缺失片 → 合并 → 完成

代码片段(Spring 部分)

java
@PostMapping("/chunk")
public ApiResp chunkUpload(@RequestParam MultipartFile file,
                           @RequestParam String fileHash,
                           @RequestParam int chunkIndex){
    String tmpDir = "/tmp/" + fileHash;
    File chunkFile = new File(tmpDir, chunkIndex + ".part");
    file.transferTo(chunkFile);

    // 更新 BitSet
    RBitSet bitSet = redisson.getBitSet("upload:" + fileHash);
    bitSet.set(chunkIndex);

    if (bitSet.cardinality() == totalChunks) {  // 全部到齐
        File target = new File("/data", fileHash);
        mergeChunks(tmpDir, target);
        if (sha256(target).equals(fileHash))
            return ApiResp.ok(target.getName());
        else throw new RuntimeException("合并后 hash 不一致");
    }
    return ApiResp.accept();
}